package edu.kit.csl.cratylus.extraction;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;

import edu.kit.csl.cratylus.datatypes.BadParameterValueException;
import edu.kit.csl.cratylus.datatypes.Candidate;
import edu.kit.csl.cratylus.datatypes.Word;
import edu.kit.csl.cratylus.io.Logger;
import edu.kit.csl.cratylus.ui.Configuration;

/**
 * This merger uses the nbest-lattice tool from the SRILM language modeling
 * toolkit to extract a good representative phoneme sequence from the
 * candidate set. When this merger is used, the parameter mergerToolPath has to
 * reference the nbest-lattice binary, which sources can be downloaded from the 
 * <a href="http://www.speech.sri.com/projects/srilm/">SRILM homepage</a>. With
 * the parameter mergerToolArgs it is possible to pass through command line
 * arguments to nbest-lattice.
 */
public class SrilmMerger implements Merger {
	
	private String nbestLatticeCmd;
	private final String TMP_FILE = "/tmp/cratylus.tmp."
			+ System.currentTimeMillis();
	private Logger log;
	private Merger fallbackMerger;
	
	/**
	 * Sole constructor. Fetches the parameter mergerToolPath and 
	 * mergerToolArgs and does some sanity checks.
	 * 
	 * @throws BadParameterValueException if the path to the tool does not
	 * 		exist or it is not executable
	 */
	public SrilmMerger() throws BadParameterValueException {
		fallbackMerger = new FrequencyMerger();
		Configuration conf = Configuration.getSingleton();
		nbestLatticeCmd = conf.getString("mergerToolPath").trim();
		File file = new File(nbestLatticeCmd);
		if (!file.canExecute()) {
			throw new BadParameterValueException("The mergerToolPath parameter"
				+ " does not point to an executable file: " + nbestLatticeCmd);
		}
		nbestLatticeCmd += " " + conf.getString("mergerToolArgs").trim()
				+ " -nbest " + TMP_FILE;
		log = Logger.getSingleton();
	}

	/**
	 * Runs SRILM's nbest-lattice tool to find a representative word for the
	 * set of candidates. If an error occurs, return the first element in the
	 * list and register error messages.
	 * 
	 * @param candidates the set of candidates
	 * @return a word representative for all candidates in the given set
	 */
	@Override
	public Word merge(List<Candidate> candidates) {
		Candidate firstCandidate = candidates.get(0);
		firstCandidate.getWord().setFixed(true);
		if (candidates.size() == 1) {
			return firstCandidate.getWord();
		}
		if (candidates.size() > 1000) { // nbest-lattice cannot handle this
			return fallbackMerger.merge(candidates);
		}
		if (candidates.get(0).isNoise()) {
			firstCandidate.getWord().setNoise(true);
			return firstCandidate.getWord();
		}
		try { 
			FileWriter fstream = new FileWriter(TMP_FILE);
			BufferedWriter out = new BufferedWriter(fstream);
			for (Candidate candidate : candidates) {
				out.write("(1) " 
						+ candidate.getWord().getContent().trim() + "\n");
			}
			out.close();
			String result = runNbestLattice();
			if (result != null) {
				result = result.trim();
				if (!result.isEmpty()) {
					Word retWord = new Word(result.trim(), true);
					retWord.setRelatedWord(
							candidates.get(0).getWord().getRelatedWord());
					return retWord;
				}
			}
		} catch (IOException e) {
			log.error("IOException while executing nbest-lattice: " 
					+ e.getMessage());
		}
		catch (InterruptedException e) {
			log.error("Program execution of nbest-lattice interrupted: " 
					+ e.getMessage());
		}
		return fallbackMerger.merge(candidates);
	}

	/*
	 * Runs nbest-lattice and returns the result
	 */
	private String runNbestLattice() throws IOException, InterruptedException {
	      String ret = null;
	      Process p = Runtime.getRuntime().exec(nbestLatticeCmd);
	      BufferedReader bri = new BufferedReader
	        (new InputStreamReader(p.getInputStream()));
	      BufferedReader bre = new BufferedReader
	        (new InputStreamReader(p.getErrorStream()));
	      if ((ret = bri.readLine()) == null) {
	    	  log.error("nbest-lattice: no output");
	      } else if (bri.readLine() != null) {
	    	  log.error("nbest-lattice: multiple lines");
	    	  ret = null;
	      }
	      bri.close();
	      String line;
	      while ((line = bre.readLine()) != null) {
	    	  log.error("nbest-lattice stderr: " + line);
	      }
	      bre.close();
	      p.waitFor();
	      return ret;
	}
}
